home *** CD-ROM | disk | FTP | other *** search
- /* -------------------------------------------------------------------------- **
- * Program: AuEncode - UUencoding for the Amiga.
- *
- * Written by Christopher R. Hertel; January, 1993
- * crh@bubble.mooses.affinity.mn.org
- * -------------------------------------------------------------------------- **
- * Copyright (C) 1993 Christopher R. Hertel
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- * -------------------------------------------------------------------------- **
- *
- * $Log: AuEncode.c,v $
- * Revision 1.2 93/09/15 20:43:07 CRH
- * Updated the comments a bit. Nothing significant.
- *
- * Revision 1.1 93/09/15 20:29:43 CRH
- * Rewritten to match format of UUENCODE. This meant that several
- * large chunks were removed. (They were, in fact, unnecessary fluf).
- * CRH.
- *
- * Revision 1.0 93/03/04 00:37:38 CRH
- * Initial revision
- *
- * -------------------------------------------------------------------------- **
- * My understanding of uuencoding/decoding is based on the knowledge that I
- * gained by reading the uuencode source provided with AmigaUUCP V1.16.
- * Many thanks to the authors of that software! The following are comments
- * from their code, which are included as my way of giving credit where
- * credit is due.
- *
- * /begin/
- * [uuencode.c:]
- * Written by Mark Horton
- * Modified by ajr (Alan J Rosenthatl,flaps@utcsri.UUCP) to use checksums
- * Modified by fnf (Fred Fish,well!fnf) to use Keith Pyle's suggestion for
- * compatibility
- * Modified by bcn (Bryce Nesbitt,ucbvax!hoser!bryce) to enable CTRL-C for
- * Amiga Lattice C. Added a transparent file size trailer for later check.
- * Changed fopen from "r" to "rb" for Messy-dos machines (thanks to Andrew
- * Wylie)
- *
- * [uudecode.c:]
- * Modified by bcn (Bryce Nesbitt,ucbvax!cogsci!bryce) to fix a misleading
- * error message on the Amiga port, to fix a bug that prevented decoding
- * certain files, to work even if trailing spaces have been removed from a
- * file, to check the filesize (if present), to add some error checking, to
- * loop for multiple decodes from a single file, and to handle common
- * BITNET mangling. Kludged around a missing string function in Aztec
- * C. Changed "r" to "rb" and "w" to "wb" for Messy-dos machines
- * (Thanks to Andrew Wylie).
- * /end/
- *
- * Also, many thanks to Michael B. Smith, current keeper of AmigaUUCP.
- * -------------------------------------------------------------------------- **
- * This program uses the AuCode module, which contains most of the uuencoding
- * functionality. 90% of *this* file is dedicated to command line parsing,
- * file I/O, and error checking. This file was written to compile under SAS
- * Amiga C 5.10 or later. I use the following command line:
- *
- * 1> lc -L AuEncode.c AuCode.c
- *
- * The primary purpose of this program is to demonstrate the utility of the
- * AuCode module.
- *
- * CRH
- * -------------------------------------------------------------------------- **
- */
-
- #include <stdio.h> /* Standard I/O */
- #include <string.h> /* Standard string functions */
- #include <stdlib.h> /* Standard C functions. */
- #include <time.h> /* C time functions. */
- #include "AuCode.h" /* Amiga-Unix uuencoding module. */
-
- /* -------------------------------------------------------------------------- **
- * Defines...
- *
- * DEFMODE - Default file mode. This value is used on Unix systems.
- * It represents Unix file protections. The default value
- * will work in most cases.
- * InBUFRSIZE - Default input buffer size. As with buffer sizes provided
- * via the command line, this buffer size will be adjusted
- * to the greatest whole multiple of au_cdMAXPhrase that is
- * less than or equal to the original value.
- * FALSE - Zero, the standard C value for not true.
- * TRUE - If it's not false, it must be true.
- * DEF_DefName - The "default" default output file name. This is the
- * target filename associated with the encoded file. If no
- * name is specified, the value assigned to this constant
- * will be used.
- */
- #define DEFMODE 0644
- #define InBUFRSIZE 1024
- #define FALSE 0
- #define TRUE (~0)
- #define DEF_DefName "AuEncode.out"
-
- /* -------------------------------------------------------------------------- **
- * Macros...
- * AdjIBufrSize - The input parameter is the requested (or default) input
- * buffer size. Our goal is a buffer size that is a whole
- * multiple of au_cdMAXPhrase (see AuCode.h), so we integer
- * divide then multiply. The result is a value that is less
- * than or equal to the original (4096, in this case), and a
- * whole multiple of au_cdMAXPhrase.
- * AdjOBufrSize - The input parameter is the adjusted Input Buffer Size.
- * Roughly, we calculate the number of Phrases (see
- * AuCode.h) per input buffer, multiply that by 3 (the
- * number of overhead characters generated per Phrase), and
- * then add that number to the number of regular encoded
- * bytes that will be generated. Then add two for good
- * luck.
- * Max - This is your basic max() macro. I implement it here in
- * order to avoid the inclusion of otherwise unnecessary
- * header files.
- */
- #define AdjIBufrSize(B) (((B)/au_cdMAXPhrase)*au_cdMAXPhrase)
- #define AdjOBufrSize(B) ((((B)/au_cdMAXPhrase)*3)+((((B)+2)/3)*4)+2)
- #define Max(A,B) (((A)>(B))?(A):(B))
-
- /* -------------------------------------------------------------------------- **
- * Typedefs...
- *
- * boolean - Your basic True/False options.
- */
-
- typedef unsigned char boolean;
-
- /* -------------------------------------------------------------------------- **
- * Globals...
- * vers - Program version string.
- * HelpTxt - An array of help strings. Should be replaced by a localization
- * file.
- */
-
- const char *vers =
- "\0$VER: AuEncode v1.2.";
-
- const char *HelpTxt[] =
- {
- "This program encodes files in uuencode format. The encoded format consists",
- "of printable characters, which may be transmitted via electronic mail.\n",
- "Usage:\tAuEncode [<infile] [>outfile] [filename] [options]\n",
- "where [<infile] = redirected input. AuEncode reads from standard",
- " input (stdin).",
- " [>outfile] = redirected output. AuEncode writes encoded output to",
- " standard output (stdout).",
- " [filename] = specify the encoded filename. Uuencoded files",
- " contain a default target filename, which is generally",
- " the same as the name of the original input file. The",
- " uuDEcoding program will use this name as the default",
- " output filename. If you do not specify a filename, a",
- " default filename will be used.",
- " [options] = command line switches, as follows:\n",
- " -? or -h = display this message.",
- " -b<bufrsize> = set the size of the input buffer. The output",
- " buffer size will be adjusted accordingly. Note",
- " that your specified buffer size will be adjusted",
- " for greatest efficiency.",
- " -m<mode> = file mode. Unix systems (and possibly some",
- " others) use this to indicate read/write/etc.",
- " protections that are to be applied to newly",
- " created files. This program provides a suitable",
- " default. Enter mode as a C style octal numeric",
- " value (i.e., with a leading zero).",
- " Example: -m0644",
- " -s = status display. The encoder will display the",
- " converted byte count (via standard error) as",
- " encoding progresses.\n",
- NULL
- }; /* HelpTxt[] */
-
- struct {
- char *defFname; /* Default file name, stored with encoded file. */
- boolean foundName; /* TRUE if defFname was read from command line. */
- boolean statusFlg; /* Flag: if TRUE, display status info. */
- long bSize; /* Input buffer size. */
- int mode; /* Unix file mode to be stored with encoded file. */
- } Args
- = { DEF_DefName, /* Initialize to default filename. */
- FALSE, /* Filename not yet read from cmd line. */
- FALSE, /* No status (do not display status). */
- InBUFRSIZE, /* Initialize input buffer size to default.*/
- DEFMODE }; /* File mode (set to default value). */
-
- /* -------------------------------------------------------------------------- **
- * Functions...
- */
-
- static void HelpMsg(void)
- /* ------------------------------------------------------------------------ **
- * This function sends the help message text to stdout. I use stdout for
- * two reasons:
- * 1) stdout is safe because the user *asked* for help.
- * 2) The number of displayable characters varies depending
- * on font, window size, etc. Via stdout, the help info
- * can be redirected to a file and viewed with a utility
- * such as <more>.
- *
- * Notes: For the Amiga, this code could be replaced with calls to
- * locale.library under OS version 2.1 or greater.
- */
- {
- int i;
-
- printf( "[%s]\n\n", &vers[2] );
- for( i = 0; HelpTxt[i]; i++ )
- printf( "%s\n", HelpTxt[i] );
- } /* HelpMsg */
-
- static boolean ParseCmdLine( int argc, char *argv[] )
- /* ------------------------------------------------------------------------ **
- * This function parses and responds to command line input.
- *
- * Input: argc - Count of arguments in argv.
- * argv - An array of pointers to character strings.
- * Basically, the previous two are the same standard C
- * input parameters that are fed into main().
- * ArgsPtr - A pointer to a CLI_Args structure. This record will
- * store the results of the parse.
- *
- * Output: A boolean. TRUE if we parsed successfully and can continue
- * processing, else FALSE.
- *
- * Notes: If there was *no* command line input, then we were run from the
- * workbench. We don't know how to do that yet, so we error out.
- * If only one argument, we display a brief message to stderr, and
- * just keep on going (in case the user used redirection).
- * If the first argument is a '?', or if a -h or -? switch is read
- * from the command line, we will dump the error text and exit.
- */
- {
- int i;
-
- if( 0 == argc ) /* No arguments = Amiga Workbench */
- return( FALSE );
-
- if( 1 == argc ) /* One argument may be redirected I/O. */
- { /* Output to stderr still safe. */
- fprintf( stderr, "[%s]\t Type '%s ?' for help.\n", &vers[2], argv[0] );
- fprintf( stderr, "Copyright (C) 1993 Christopher R. Hertel\n" );
- fprintf( stderr, "Please see the Gnu General Public License, " );
- fprintf( stderr, "version 2,\nfor terms and conditions.\n" );
- fprintf( stderr, "Ctrl-C <return> to cancel.\n" );
- return( TRUE ); /* Assume redirected I/O. */
- }
-
- if( '?' == argv[1][0] ) /* If first character of arg #1 is a '?'... */
- { /* ...then send help (and exit). */
- HelpMsg();
- return( FALSE );
- }
-
- for( i = 1; i < argc; i++ ) /* For each command line parameter */
- {
- char *Source = argv[i];
-
- if( '-' == *Source )
- switch( Source[1] )
- {
- case '?': /* Dump help & return. */
- case 'H':
- case 'h':
- HelpMsg();
- return( FALSE );
- case 'B': /* Read new buffer size. */
- case 'b':
- sscanf( &(Source[2]), "%ld", &(Args.bSize) );
- break;
- case 'M': /* Read new mode setting. */
- case 'm':
- sscanf( &(Source[2]), "%o", &(Args.mode) );
- break;
- case 'S': /* Turn on status flag. */
- case 's':
- Args.statusFlg = TRUE;
- break;
- default: /* error */
- fprintf( stderr, "? Invalid option: %s\n", Source );
- } /* switch */
- else
- /* Assume that it's a filename. */
- {
- if( Args.foundName )
- fprintf( stderr, "? Extra filename? [%s]\n", Source );
- else
- {
- Args.defFname = Source;
- Args.foundName = TRUE;
- } /* else */
- } /* else */
- } /* for */
-
- return( TRUE );
- } /* ParseCmdLine */
-
- static long Encode( long inBsize, boolean Stats )
- /* ------------------------------------------------------------------------ **
- * The actual encoding of the source file is performed here.
- *
- * Input: inBsize - Size of the input buffer. This value will be modifed
- * so that it is a multiple of the Phrase size. The
- * output buffer size will be calculated based on the
- * resultant size of the input buffer. The calculations
- * result in optimized input/output buffer sizes.
- * Stats - Boolean. If TRUE, statistics will be displayed via
- * stderr.
- *
- * Output: The total number of converted (input) bytes.
- */
- {
- char *p; /* Position within sBufr pointer. */
- int readcnt, /* Number of bytes received (input) per fread() call. */
- codecnt; /* Used to count encoded bytes per call to au_cdEncode()*/
- long total = 0; /* Running total number of input bytes. */
- long outcount = 0; /* Running total number of generated (output) bytes. */
- long t; /* Time value, used when displaying stats. */
- long outBsize; /* Calculated size of the output buffer. */
- char *sBufr; /* Pointer to the source (input) buffer. */
- char *tBufr; /* Pointer to the target (output) buffer. */
-
- inBsize = AdjIBufrSize(inBsize); /* Calc correct input buffer size.*/
- inBsize = Max( inBsize, au_cdMAXPhrase ); /* Make sure size > 0. */
- outBsize = AdjOBufrSize(inBsize); /* Calc correct output buffer size*/
- sBufr = (char *)malloc( inBsize ); /* Allocate input buffer. */
- tBufr = (char *)malloc( outBsize ); /* Allocate output buffer. */
- if( !sBufr || !tBufr ) /* If either allocation failed... */
- {
- fprintf( stderr, "? Buffer allocation failure.\n" );
- if( sBufr ) free( sBufr );
- if( tBufr ) free( tBufr );
- return( 0L );
- }
-
- if( Stats ) /* If Stats requested, display buffer sizes and get time. */
- {
- fprintf( stderr,
- "Input buffer: [%ld] bytes. Output buffer: [%ld] bytes.\n",
- inBsize, outBsize );
- t = time( NULL );
- }
-
- while( readcnt = fread( sBufr, 1, inBsize, stdin ) ) /* Read a chunk. */
- {
- codecnt = 0; /* We've encoded 0 bytes of the input buffer. */
- p = sBufr; /* Point to start of input buffer. */
- while( readcnt = (readcnt-codecnt) ) /* Encode as much as possible. */
- { /* If any are left over we'll loop. */
- codecnt = au_cdEncode( p, readcnt, tBufr, outBsize );
- p = &p[codecnt]; /* Point to first non-encoded byte */
- /* within sBufr. */
- total += codecnt; /* Add encoded byte count to total. */
- fputs( tBufr, stdout ); /* Write 'em. */
-
- if( Stats ) /* Running statistics. */
- {
- outcount += strlen( tBufr );
- fprintf( stderr, "Read: [%ld], Written: [%ld].\r", total, outcount );
- }
- }
- }
-
- if( Stats ) /* Finished: concluding statistics. */
- {
- t = time(NULL)-t;
- fprintf( stderr, "Total: %ld bytes read & encoded, ", total );
- fprintf( stderr, "%ld bytes written ", outcount );
- if( t )
- fprintf( stderr, "in %ld second%s.\n", t, (1==t)?(""):("s") );
- else
- fprintf( stderr, "in less than one second.\n" );
- }
- return( total );
- } /* Encode */
-
- int main( int argc, char **argv )
- /* ------------------------------------------------------------------------ **
- * Mainline.
- *
- * The following steps are performed:
- * 1) Parse the command line.
- * 2) Write the encoding header.
- * 3) Encode the file (stdin to stdout).
- * 4) Write the encoding trailer.
- *
- * ------------------------------------------------------------------------ **
- */
- {
- long ByteCount; /* Total bytes read from input. */
-
- if( ParseCmdLine( argc, argv ) ) /* Parse the command line. */
- {
- /* Write code header to output. */
- printf( "\nbegin %o %s\n", Args.mode, Args.defFname );
-
- /* Encode the input file to the output file. */
- ByteCount = Encode( Args.bSize, Args.statusFlg );
-
- /* Write code trailer to output. */
- printf( "``\nend\nsize %ld\n", ByteCount );
- }
- return( 0 );
- } /* main */
-
- /* ========================================================================== */
-